import { 
  Component, 
  OnInit, 
  ViewChild, 
  ElementRef, 
  Renderer2,
  HostListener
} from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import { NzModalService } from 'ng-zorro-antd';

import { CustomLayer } from '../../../../models';
import { LayersService, DrawSelectedStoreService, InteractionSelectService } from '../../../../services';
import { ImportDataComponent } from '../../../import-data/import-data.component';
import { MapComponent } from '../../../../../ngx-ol/components/map/map.component';
import { State } from '../../../../../store';
import { InitialLayers, RemoveLayer, LayersSort, LayerVisible, LayerInvisible, LayersClean, LayerAddTable, LayerSourceLoaded, LayerSourceLoadError, LayerSourceLoading, LayerAddQueryResult, LayerRemoveQueryResult, LayerFieldTypes } from '../../../../store/actions/layers.action';
import * as fromLayersSelector from '../../../../store/selectors/layer.selector';
import { Layer } from '../../../../class';
import { LayerTypes } from '../../../../enums';
import { PropertiesQueryComponent } from '../../../properties-query';
import { isObject } from 'util';
import { LocalstoreService } from '../../../../../shared/services';
import { UserToken } from '../../../../../shared/modes';

@Component({
  selector: 'ege-map-layers-management',
  templateUrl: './layers-management.component.html',
  styleUrls: ['./layers-management.component.scss', '../../components/ngx-dnd/ngx-dnd.scss']
})
export class LayersManagementComponent implements OnInit {

  private bottom: number = 0;

  spatialQueryUseable$: Observable<boolean>;
  total$: Observable<number>;
  layers$: Observable<Layer[]>;
  datas$: Observable<Layer[]>;

  layers: { [id: string]: CustomLayer } = {};

  @ViewChild('containerElement') containerElement: ElementRef;

  @HostListener('window:resize', ['$event'])
  onWindowResize(event): void {
    this.setContainerElementHeight();
  }

  constructor(
    private store: Store<State>,
    private renderer: Renderer2,
    private model: NzModalService,
    private mapComponent: MapComponent,
    private layersService: LayersService,
    private interactionSelectService: InteractionSelectService,
    private drawSelectedStoreService: DrawSelectedStoreService
  ) { }

  ngOnInit() {
    const map = this.mapComponent.instance;
    this.setContainerElementHeight();
    this.interactionSelectService.setMap(map);
    // load default layers
    this.store.dispatch(new InitialLayers());
    // subscript layers and create layers
    this.layers$ = this.store.select(fromLayersSelector.getAllLayers);
    this.datas$ = this.store.select(fromLayersSelector.getAllDatas);
    this.store.select(fromLayersSelector.getAll).subscribe((_layers: Layer[]) => this.layersBuilder(_layers));
    // subscript last remove layer and remove it
    this.store.select(fromLayersSelector.getLastRemoveLayer).subscribe((layers: Layer[]) => {
      if(layers) {
        this.removeLayers(layers);
      }
    });
    // get useable that layers can do spatial query
    this.spatialQueryUseable$ = this.store.select(fromLayersSelector.getLayersSpatialQueryUseable);
    // get layers length
    this.total$ = this.store.select(fromLayersSelector.getLayersTotal);
    // subscript selected feature
    this.store.select(fromLayersSelector.getSelectedFeature).subscribe(selected => selected && this.featureHighlight(selected.layerId, selected.featureId));
    // subscript deselected feature
    this.store.select(fromLayersSelector.getDeselectedFeature).subscribe(deselected => deselected && this.removeFeatureHighlight(deselected.layerId, deselected.featureId));
    this.store.select(fromLayersSelector.getTableState).subscribe(state => this.setBottomHeight(state));
  }

  onCheckedChange(model: boolean, layer: CustomLayer): void {
    if(model) {
      this.store.dispatch(new LayerVisible(layer.id));
    } else {
      this.store.dispatch(new LayerInvisible(layer.id));
    }
  }

  onSpaceQuery(layer: Layer): void {
    const features = this.drawSelectedStoreService.selected();
    if(this.layers[layer.id]) {
      this.layers[layer.id].spaceQuery(features);
    }
  }

  openTable(layer: CustomLayer): void {
    this.store.dispatch(new LayerAddTable(layer.id));
  }

  onReload(layer: Layer): void {
    if(this.layers[layer.id]) {
      this.layers[layer.id].reload();
    }
  }

  onRemove(layer: Layer): void {
    this.store.dispatch(new RemoveLayer(layer.id));
  }

  onLayersSortChange(event): void {
    const layers: CustomLayer[] = event.model;
    const sort = layers.reverse().map(layer => layer.id);
    this.store.dispatch(new LayersSort(sort));
  }

  onConfirmClear(): void {
    this.store.dispatch(new LayersClean());
  }

  onClickImportData(): void {
    this.model.open({
      title: '导入数据',
      width: '520px',
      content: ImportDataComponent,
      footer: false,
      maskClosable: false,
      componentParams: {}
    })
    .subscribe(result => console.log(result))
  }

  private setBottomHeight(state): void {
    switch(state) {
      case 'visible': {
        this.bottom = 340;
        break;
      }
      case 'hidden': {
        this.bottom = 55;
        break;
      }
      default: {
        this.bottom = 0;
        break;
      }
    }
    this.setContainerElementHeight();
  }

  private setContainerElementHeight():void {
    const windowHeight: number = window.innerHeight;
    const height: number = windowHeight - this.bottom - 100;
    this.renderer.setStyle(this.containerElement.nativeElement, 'max-height', `${height}px`);
    // const windowHeight: number = window.innerHeight;
    // const bodyheight: number = (windowHeight - 218 - this.bottom) / 2;
    // const bodyElements: HTMLElement[] = this.containerElement.nativeElement.querySelectorAll('.layers-management-body'); 
    // bodyElements.forEach((element: any) => {
    //   this.renderer.setStyle(element, 'max-height', `${bodyheight}px`);
    // });
  }

  private layersBuilder(layers: Layer[]): void {
    const map = this.mapComponent.instance;
    this.layers = layers.reduce((_layers, _layer) => {
      if(!_layers[_layer.id]) {
        _layers[_layer.id] = new CustomLayer(map, this.layersService, _layer);
        _layers[_layer.id].on('loading').subscribe(() => this.store.dispatch(new LayerSourceLoading(_layer.id)));
        _layers[_layer.id].on('loaded').subscribe(data => this.store.dispatch(new LayerSourceLoaded({ id: _layer.id, data: data })));
        _layers[_layer.id].on('loadError').subscribe(error => this.store.dispatch(new LayerSourceLoadError({ id: _layer.id, error: error })));
        _layers[_layer.id].on('query').subscribe(ids => this.store.dispatch(new LayerAddQueryResult({ id: _layer.id, result: ids })));
        _layers[_layer.id].on('fieldTypes').subscribe(fieldTypes => this.store.dispatch(new LayerFieldTypes({ id: _layer.id, fieldTypes })));
      } else {
        _layers[_layer.id].setOpacity(_layer.opacity);
        _layers[_layer.id].setVisible(_layer.visiable);
        _layers[_layer.id].setZIndex(_layer.zIndex);
        _layers[_layer.id].updateQueryResult(_layer.queryResult);
      }
      if(_layers[_layer.id].selectable) {
        if(_layers[_layer.id].visiable) {
          this.interactionSelectService.addLayer(_layers[_layer.id].id, _layers[_layer.id]);
        } else {
          this.interactionSelectService.removeLayer(_layers[_layer.id].id);
        }
      }
      return _layers;
    }, { ...this.layers });
  }

  private removeLayers(layers: Layer[]): void {
    layers.forEach(_layer => {
      const { [_layer.id]: removed, ...layers } = this.layers;
      removed.remove();
      this.interactionSelectService.removeLayer(_layer.id);
      this.layers = layers;
    });
  }

  private featureHighlight(layerId: string, featureId: string): void {
    const feature = this.findFeature(layerId, featureId);
    if(feature) {
      feature.set('selected', true);
    }
  }

  private removeFeatureHighlight(layerId: string, featureId: string): void {
    const feature = this.findFeature(layerId, featureId);
    if(feature) {
      feature.set('selected', false);
    }    
  }

  private findFeature(layerId: string, featureId: string): any {
    const layer = this.layers[layerId];
    let feature = null;
    if(layer) {
      feature = layer.getFeatureById(featureId);
    }
    return feature;
  }

  private cancelPropertyQuery(id: string): void {
    if(this.layers[id]) {
      this.layers[id].removePropertyQuery();
    }
  }

  private cancelSpaceQuery(id: string): void {
    if(this.layers[id]) {
      this.layers[id].removeSpaceQuery();
    }
  }

  private cancelQuery(id: string): void {
    this.store.dispatch(new LayerRemoveQueryResult(id));
    if(this.layers[id]) {
      this.layers[id].removeQuery();
    }
  }

  private onPropertiesQuery(layer: CustomLayer): void {
    const model = this.model.open({
      title: `${layer.title}-过滤条件设置`,
      content: PropertiesQueryComponent,
      width: '780px',
      maskClosable: false,
      footer: false,
      componentParams: {
        fields: layer.fieldTypes,
        initial: layer.getQueryRules()
      }
    });
    const subscribe = model.subscribe(data => {
      if(isObject(data)) {
        layer.propertiesQuery(data.rules.length > 0 ? data : null, () => {
          subscribe.unsubscribe();
          model.destroy('onOk');
        });
      } else if(data === 'onCancel') {
        subscribe.unsubscribe();
      }
    });
  }

  private getExtent(id: string): number[] {
    return this.layers[id] ? this.layers[id].getExtent() : null;
  }

  private zoomToLayerExtent(id: string): void {
    this.layers[id] && this.layers[id].zoomToLayerExtent();
  }

}
